package com.longway.common.service;

import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.ehcache.CacheKit;
import com.jfinal.plugin.ehcache.IDataLoader;
import com.longway.common.constant.Const;
import com.longway.common.model.*;
import com.longway.core.toolbox.Func;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class BusinessService extends BaseService{
	
	private static final String excludecacheUrl = "/admin/cache/";
	private static final String excludeztreeUrl = "/admin/ztree/";
	private static final String indexUrl = "/admin";
	
	public static final BusinessService me = new BusinessService();
	
	private BaseMenu menuDao = new BaseMenu().dao();
	private BaseRole baseRoleDao = new BaseRole().dao();
	private BaseDept baseDeptDao = new BaseDept().dao();
	private BaseSetting baseSettingDao = new BaseSetting().dao();
			
	public List<BaseMenu> getMenusByUserIdAndRoleId(Long userId,String roleId){
		Record roleExt = Db.findFirst("select * from base_role_ext where userId=?", userId);
		String roleIn = roleExt==null? "" : roleExt.getStr("rolein");
		String roleOut = roleExt==null? "" : roleExt.getStr("roleout");
		StringBuilder sql = new StringBuilder(); 
		List<String> params = new ArrayList<String>();
		sql.append("select * from base_menu  ");
		sql.append("where status=1 and (icon is not null and icon not LIKE '%btn%' and icon not LIKE '%icon%') ");
		sql.append("and (id in (select menuId from base_relation where roleId in (");
		int i =0;
		for(String roleid : roleId.split(",")){
			i++;
			sql.append("?");
			if(i!=roleId.split(",").length){
				sql.append(",");
			}
			params.add(roleid);
		}
		sql.append(")) ");
		if(StrKit.notBlank(roleIn)){
			sql.append("or id in ("); //?) 
			i = 0;
			for(String menuid : roleIn.split(",")){
				i++;
				sql.append("?");
				if(i!=roleIn.split(",").length){
					sql.append(",");
				}
				params.add(menuid);
			}
			sql.append(")");
		}
		sql.append(") ");
		if(StrKit.notBlank(roleOut)){
			sql.append("and id not in( "); //?)
			i = 0;
			for(String menuid : roleOut.split(",")){
				i++;
				sql.append("?");
				if(i!=roleOut.split(",").length){
					sql.append(",");
				}
				params.add(menuid);
			}
			sql.append(") ");
		}
		sql.append("order by levels,pCode,num ");
		List<BaseMenu> userMenus = menuDao.find(sql.toString(), params.toArray());
		return userMenus;
	}
	
	public List<BaseMenu> getUserRightMenusByCode(Long userId,String roleId,String code) {
		Record roleExt = Db.findFirst("select * from base_role_ext where userId=?",userId);
		String roleIn = roleExt==null? "" : roleExt.getStr("rolein");
		String roleOut = roleExt==null? "" : roleExt.getStr("roleout");
		final StringBuilder sql = new StringBuilder();
		List<Object> params = new ArrayList<Object>();
		params.add(code);
		params.add(code);
		sql.append("select base_menu.* ,(select name from base_menu where code=?) as pname  from base_menu");
		sql.append(" where ( ");
		sql.append("	 (status=1)");
		sql.append("	 and (icon is not null and (icon like '%btn%' or icon like '%icon%' ) ) ");
		sql.append("	 and (url like '%add%' or url like '%edit%' or url like '%remove%'  or url like '%del%' or url like '%view%' ) ");
		sql.append("	 and (pCode=?)");
		sql.append("	 and (id in (select menuId from base_relation where roleId in (");   //?)) or id in (?))");
		int i =0;
		for(String roleid : roleId.split(",")){
			i++;
			sql.append("?");
			if(i!=roleId.split(",").length){
				sql.append(",");
			}
			params.add(roleid);
		}
		sql.append(")) ");
		if(StrKit.notBlank(roleIn)){
			sql.append("or id in ("); //?) 
			i = 0;
			for(String menuid : roleIn.split(",")){
				i++;
				sql.append("?");
				if(i!=roleIn.split(",").length){
					sql.append(",");
				}
				params.add(menuid);
			}
			sql.append(")");
		}
		sql.append(") ");
		if(StrKit.notBlank(roleOut)){
			sql.append("and id not in( "); //?)
			i = 0;
			for(String menuid : roleOut.split(",")){
				i++;
				sql.append("?");
				if(i!=roleOut.split(",").length){
					sql.append(",");
				}
				params.add(menuid);
			}
			sql.append(") ");
		}
		sql.append(") ");
		sql.append(" order by num");
		
		return menuDao.find(sql.toString(), params.toArray());
	}
	
	
	public BaseUser getLoginAccountWithSessionId(String sessionId) {
		return CacheKit.get(Const.loginAccountCacheName, sessionId);
	}
	
	
	public BaseUser loginWithSessionId(String sessionId, String loginIp) {
		BaseSession session = BaseSession.dao.findById(sessionId);
		if (session == null) {      // session 不存在
			return null;
		}
		if (session.isExpired()) {  // session 已过期
			session.delete();		// 被动式删除过期数据，此外还需要定时线程来主动清除过期数据
			return null;
		}
		BaseUser loginAccount =  new BaseUser().dao().findById(session.getAccountId());
		// 找到 loginAccount 并且 是正常状态 才允许登录
		if (loginAccount != null && loginAccount.isStatusOk()) {
			loginAccount.removeSensitiveInfo();                                 // 移除 password 与 salt 属性值
			loginAccount.put("sessionId", sessionId);                          // 保存一份 sessionId 到 loginAccount 备用
			BusinessService.me.joinRoleName(loginAccount);
			BusinessService.me.joinDeptName(loginAccount);
			CacheKit.put(Const.loginAccountCacheName, sessionId, loginAccount);
			String msg = Func.format("[sessionID]: {} [sessionHost]: {}", session.getId(),loginIp);
			createLoginLog("登录",loginAccount.getId(),"LoginSessionInterceptor","loginWithSessionId","1",msg, loginIp);
			return loginAccount;
		}
		return null;
	}
	
	public void joinRoleName(Model model) {
		joinRole("roleId", model, "roleName");
	}
	
	public void joinDeptName(Model model) {
		joinDept("deptId", model, "deptName");
	}


	public void join(String joinOnField, List<? extends Model> modelList, String... joinAttrs) {
		if (modelList != null) {
			for (Model m : modelList) {
				joinRole(joinOnField, m, joinAttrs);
			}
		}
	}

	/**
	 * 在 User 等模块，需要关联查询获取 Role 对象的 name时使用此方法
	 * 避免使用关联查询，优化性能，在使用中更关键的地方在于缓存的清除
	 * @param joinOnField join 使用的字段名
	 * @param model 需要 join 的 model
	 * @param joinAttrs 需要 join 到 model 中的的属性名称
	 */
	public void joinRole(String joinOnField, Model model, String... joinAttrs) {
		String roleIds = model.getStr(joinOnField);
		if(roleIds == null) {
			throw new RuntimeException("Model 中的 \"" + joinOnField  + "\" 属性值不能为 null");
		}
		String[] roleIdsArray = roleIds.split(",");
		for(String roleId : roleIdsArray){
			BaseRole baseRole = CacheKit.get(Const.ROLE_CACHE, "findById_"+roleId);
	        if (baseRole == null) {
	        	baseRole = baseRoleDao.findById(roleId);
	            if (baseRole != null) {
	                CacheKit.put(Const.ROLE_CACHE, "findById_"+roleId, baseRole);
	            }
	        }
	    	if (baseRole != null) {
				for (String attr : joinAttrs) {
					model.put(attr, model.get(attr)+" "+baseRole.get(attr));
				}
			}else {
				throw new RuntimeException("未找到 BaseRole ,BaseRole 的 id 值为：" + roleId + " 可能是数据库数据不一致");
			}
		}
    	
	}
	
	public void joinDept(String joinOnField, Model model, String... joinAttrs) {
		Long deptId = model.getLong(joinOnField);
		if (deptId == null) {
			throw new RuntimeException("Model 中的 \"" + joinOnField  + "\" 属性值不能为 null");
		}

		// 优先从缓存中取，未命中缓存则从数据库取
		BaseDept baseDept = CacheKit.get(Const.DEPT_CACHE, "findById_"+deptId);
        if (baseDept == null) {
        	baseDept = baseDeptDao.findById(deptId);
            if (baseDept != null) {
                CacheKit.put(Const.DEPT_CACHE, "findById_"+deptId, baseDept);
            }
        }
        // join 真正开始的地方，前面是准备工作
		if (baseDept != null) {
			for (String attr : joinAttrs) {
				model.put(attr, baseDept.get(attr));
			}
		} else {
			throw new RuntimeException("未找到 baseDept ，baseDept 的 id 值为：" + deptId + " 可能是数据库数据不一致");
		}
	}
	
	private void createLoginLog(String logName ,Long userId,String className,String method,String succed,String message, String loginIp) {
		Record loginLog = new Record().set("logName",logName)
				  .set("userId", userId)
				  .set("className", className)
				  .set("operator", method)
				  .set("createTime", new Date())
				  .set("succeed", succed)
				  .set("message", message)
				  .set("ip", loginIp);
		DbService.save("base_login_log", loginLog);
	}
	
	public boolean isDoLog() {
		/*Record record = CacheKit.get(Const.SYS_CACHE, "parameter_log", new IDataLoader() {
			@Override
			public Record load() {
				return Db.findFirst("select para from base_parameter where code = ?",Const.PARA_LOG_CODE);
			}
		});
		if(record != null && JStrKit.equals(record.getStr("para"),"1")){
			return true;
		}*/
		return true;
	}
	
	public Ret saveLog(BaseOperationLog baseOperationLog) {
		super.save(baseOperationLog);
		return Ret.ok("msg", "新增成功"); 
	}
	
	public String[] logPatten() {
		String[] patten = { "login", "logout", "grant", "save", "update", "remove", "del", "delete", "restore" };
		return patten;
	}
	
	public Ret logMaps() {
		Ret ret = Ret.create()
					.set("login", "登录")
					.set("logout", "登出")
					.set("grant", "授权")
					.set("save", "新增")
					.set("update", "修改")
					.set("remove", "删除")
					.set("del", "删除")
					.set("delete", "删除")
					.set("restore", "还原");
		return ret;
	}
	
	/**
	 * 是否为超级管理员，role.id 值为 1 的为超级管理员
	 */
	public boolean isSuperAdmin(Long userId) {
		boolean isAdmin = CacheKit.get(Const.ISADMIN_CACHE, userId,
				new IDataLoader() {
					@Override
                    public Boolean load() {
						String roleIds = Db.template("admin.auth.isSuperAdmin", userId).queryStr();
						if(StrKit.notBlank(roleIds) && roleIds.contains("1")){
							return true;
						}
						return false;
					}
				});
		return isAdmin;
	}

	/**
	 * 是否拥有具体某个权限
	 */
	public boolean hasPermission(String roleIds, String actionKey) {
		if (StrKit.isBlank(actionKey)) {
			return false;
		}
		String ret = CacheKit.get(Const.PERMI_CACHE, "permi_" + actionKey + "_"+ roleIds, new IDataLoader() {
			@Override
            public String load() {
				return Db.queryStr("select actionkey from base_role_permission where roleid in ("+roleIds+") and actionkey = ? ",actionKey);
			}
		});
		return ret != null;
	}
	
	
	public boolean isExculeUrl(String actionKey) {
		if(actionKey.equals(indexUrl)||actionKey.startsWith(excludeztreeUrl)||actionKey.startsWith(excludecacheUrl)){
			return true;
		}
		return false;
	}
	
	public BaseSetting findBaseSettingByCache() {
		String sql= "select * from base_setting";
        return baseSettingDao.findFirstByCache(Const.SETTING_CACHE,Const.SETTING_CACHE_KEY, sql);
	}
}
